package com.by.component.export;

import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.by.component.export.dto.AddExportRecordResultSDTO;
import com.by.component.export.dto.AddExportRecordSDTO;
import com.by.component.export.dto.InfoExportRecordSDTO;
import com.by.component.export.dto.ItemExportRecord4PageSDTO;
import com.by.component.export.enums.ExportTypeEnum;
import com.by.component.export.mapper.ExportRecordMapper;
import com.by.datasource.transaction.AfterCommitExecutor;
import com.by.mqsender.support.ByMqSupport;
import com.by.toolkit.consts.LockConst;
import com.by.toolkit.consts.MqTagConst;
import com.by.toolkit.enums.ErrorCodeEnum;
import com.by.toolkit.enums.FlagEnum;
import com.by.toolkit.model.PageReq;
import com.by.toolkit.model.PageResp;
import com.by.toolkit.utils.ByIfNotEmptyUtil;
import com.by.toolkit.utils.SplitterUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.redisson.api.RFuture;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.by.toolkit.utils.ExceptionUtil.nullError;
import static com.by.toolkit.utils.ExceptionUtil.trueError;

@Slf4j
@DubboService
public class ExportServiceImpl implements ExportService {

    @Resource
    private ExportRecordMapper exportRecordMapper;

    @Resource
    private ByMqSupport byMqSupport;

    @Resource
    private RedissonClient redisson;

    @Override
    public PageResp<ItemExportRecord4PageSDTO> queryPage(PageReq<ItemExportRecord4PageSDTO> req) {
        Page<ItemExportRecord4PageSDTO> page = exportRecordMapper.queryPage(Page.of(req.getPageIdx(), req.getPageSize()), req.getCondition());
        return ByIfNotEmptyUtil.getIfNotEmptyOrElse(page.getRecords()
                , () -> PageResp.of(page.getTotal(), page.getRecords())
                , PageResp::ofNull);
    }

    @Override
    public AddExportRecordResultSDTO addExport(AddExportRecordSDTO dto) {
        Integer exportType = dto.getExportType();
        String exportParam = dto.getExportParam();
        Long sysUserId = dto.getSysUserId();

        ExportTypeEnum exportTypeEnum = ExportTypeEnum.resolve(exportType);
        nullError(exportTypeEnum, "导出类型不正确");

        RLock lock = redisson.getLock(SplitterUtil.EN_COLON_JOINER.join(LockConst.EXPORT_LOCK, sysUserId, exportType));
        RFuture<Boolean> lockAsync = lock.tryLockAsync(1, 1, TimeUnit.SECONDS);
        try {
            trueError(BooleanUtil.isFalse(lockAsync.join()), ErrorCodeEnum.REDIS_LOCK_FAILED);

            Long unProcessedId = exportRecordMapper.unProcessedId(sysUserId, exportType);
            trueError(Objects.nonNull(unProcessedId), "有正在处理中的导出记录");

            ExportRecord exportRecord = ExportRecord.builder()
                    .exportType(exportType)
                    .exportParam(exportParam)
                    .sysUserId(sysUserId)
                    .build();
            exportRecordMapper.insert(exportRecord);

            Long exportRecordId = exportRecord.getId();
            boolean responseExport = exportTypeEnum.isResponseExport();
            if (responseExport) {
                return AddExportRecordResultSDTO.builder()
                        .exportRecordId(exportRecordId)
                        .responseExportFlag(FlagEnum.YES.getCode())
                        .build();
            }

            AfterCommitExecutor.execute(() -> {
                byMqSupport.sendAsync(Map.of(
                        "recordId", exportRecordId,
                        "exportType", exportType,
                        "param", exportRecord.getExportParam(),
                        "sysUserId", sysUserId
                ), MqTagConst.EXPORT);
            });

            return AddExportRecordResultSDTO.builder()
                    .exportRecordId(exportRecordId)
                    .responseExportFlag(FlagEnum.NO.getCode())
                    .build();
        } finally {
            lock.unlockAsync();
        }
    }

    @Override
    public void processing(Long recordId) {
        exportRecordMapper.processing(recordId);
    }

    @Override
    public void processed(Long recordId, Long exportCount, Long ossId) {
        exportRecordMapper.processed(recordId, exportCount, ossId);
    }

    @Override
    public void failProcess(Long recordId, String failMsg) {
        exportRecordMapper.failProcess(recordId, failMsg);
    }

    @Override
    public InfoExportRecordSDTO recordInfo(Long recordId) {
        return exportRecordMapper.recordInfo(recordId);
    }
}
